package com.vhall.component.service.interactive.impl;


import cn.hutool.core.io.FileUtil;
import com.vhall.component.dao.interactive.DocumentsRoomMapper;
import com.vhall.component.entity.interactive.vo.UploadCheckChunkRespVO;
import com.vhall.component.entity.interactive.vo.UploadChunkRespVO;
import com.vhall.component.entity.interactive.vo.UploadMergeChunkRespVO;
import com.vhall.component.entity.interactive.vo.UploadRegisterRespVO;
import com.vhall.component.framework.common.exception.BusinessException;
import com.vhall.component.plugin.common.exception.BizErrorCode;
import com.vhall.component.plugin.core.service.PaasService;
import com.vhall.component.service.interactive.DocumentsUploadService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * @Author: xin.qin
 * @Date: 2022/06/27 19:55
 */
@Slf4j
@Service
public class DocumentsUploadServiceImpl implements DocumentsUploadService {

    @Autowired
    private DocumentsRoomMapper documentsRoomMapper;

    @Autowired
    private PaasService paasService;

    //@Value("${fileUploadPaht:}")
    private static final String UPLOAD_PATH = "";

    @Override
    public UploadRegisterRespVO register(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //检查文件在磁盘上是否存在

        //文件所属目录的路径
        String fileFolderPath = this.getFileFolderPath(fileMd5);
        //文件路径
        String filePath = this.getFilePath(fileMd5, fileExt);
        File file = new File(filePath);
        //文件是否存在
        boolean exists = file.exists();
        //文件不存,检查文件所在目录是否存在，不存在则创建
        File fileFolder = new File(fileFolderPath);
        if (!fileFolder.exists()) {
            fileFolder.mkdirs();
        }
        UploadRegisterRespVO registerRespVO = new UploadRegisterRespVO();
        registerRespVO.setExists(exists ? 1 : 0);

        return registerRespVO;
    }

    @Override
    public UploadCheckChunkRespVO checkChunk(String fileMd5, Integer chunk, Integer chunkSize) {
        //得到分块文件的所在目录
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
        //得到分块文件
        File chunkFile = new File(chunkFileFolderPath + chunk);
        UploadCheckChunkRespVO checkChunkRespVO = new UploadCheckChunkRespVO();
        //检查分块文件是否存在
        if (chunkFile.exists()) {
            //块文件存在
            checkChunkRespVO.setExists(1);
        } else {
            //块文件不存在
            checkChunkRespVO.setExists(0);
        }
        return checkChunkRespVO;
    }

    @Override
    public UploadChunkRespVO uploadChunk(MultipartFile file, String fileMd5, Integer chunk) {
        //得到分块文件目录
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);

        //得到分块文件路径
        String chunkFilePath = chunkFileFolderPath + chunk;

        //检查分块目录，不存在则要自动创建
        File chunkFileFolder = new File(chunkFileFolderPath);
        if (!chunkFileFolder.exists()) {
            chunkFileFolder.mkdirs();
        }

        //得到上传文件的输入流
        InputStream inputStream = null;
        FileOutputStream outputStream = null;
        try {
            inputStream = file.getInputStream();
            outputStream = new FileOutputStream(new File(chunkFilePath));
            IOUtils.copy(inputStream, outputStream);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                throw new BusinessException(BizErrorCode.BIZ_UPLOAD_FAILED);
            }
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
                throw new BusinessException(BizErrorCode.BIZ_UPLOAD_FAILED);
            }
        }
        return new UploadChunkRespVO();
    }

    @Override
    public UploadMergeChunkRespVO mergeChunks(String fileMd5, String fileName, Long fileSize, String mimetype, String fileExt) {
        //得到分块文件所属目录
        String chunkFileFolderPath = this.getChunkFileFolderPath(fileMd5);
        File chunkFileFolder = new File(chunkFileFolderPath);

        //得到分块文件列表
        File[] files = chunkFileFolder.listFiles();
        List<File> fileList = Arrays.asList(files);

        //创建合并文件路径
        String filePath = this.getFilePath(fileMd5, fileExt);
        File mergeFile = new File(filePath);

        //执行合并
        mergeFile = this.mergeFile(fileList, mergeFile);
        if (mergeFile == null) {
            //合并文件失败
            throw new BusinessException(BizErrorCode.BIZ_UPLOAD_MERGE_FAILED);
        }

        //校验文件的md5值是否和前端传入的md5一致
        boolean checkFileMd5 = this.checkFileMd5(mergeFile, fileMd5);
        if (!checkFileMd5) {
            //校验文件失败
            throw new BusinessException(BizErrorCode.BIZ_UPLOAD_MD5_CHECK_FAILED);
        }

        //文件路径保存相对路径
        String relativePath = fileMd5.substring(0, 1) + "/" + fileMd5.substring(1, 2) + "/" + fileMd5 + "/" + fileMd5 + "." + fileExt;
        UploadMergeChunkRespVO mergeChunkRespVO = new UploadMergeChunkRespVO();
        mergeChunkRespVO.setFilePath(relativePath);

        //上传PAAS云
        //PaaS端创建文档信息
//        JSONObject result = paasService.documentCreate(mergeFile);
//        if (null == result) {
//            throw new BusinessException(BizErrorCode.BIZ_UPLOAD_FAILED);
//        }
//        String documentId = result.getString("document_id");
//        Integer convertedTotal = result.getInteger("converted_total");
//        Integer convertedTime = result.getInteger("converted_time");
//        //PaaS端获取文档信息
//        JSONObject fileInfo = paasService.documentInfo(documentId);
//
//        String fileName = vo.getDocument().getOriginalFilename();
//        String hash = fileInfo.getString("hash");
//        String ext = fileInfo.getString("ext");
//
//        //入db
//        RoomDocumentsEntity doc = new RoomDocumentsEntity();
//        doc.setAppId(paasService.getAppId());
//        doc.setDocumentId(documentId);
//        doc.setAccountId(accountId.toString());
//        doc.setFileName(fileName);
//        doc.setHash(hash);
//        doc.setExt(ext);
//        doc.setCreatedAt(LocalDateTime.now());
//        doc.setUpdatedAt(LocalDateTime.now());
//        if (StringUtils.isNotBlank(vo.getRoomId())) {
//            doc.setRoomId(vo.getRoomId());
//        } else {
//            doc.setRoomId("");
//        }
//        documentsRoomMapper.insert(doc);
//
//        DocumentUploadRsp rsp = new DocumentUploadRsp();
//        rsp.setConvertedTime(convertedTime);
//        rsp.setConvertedTotal(convertedTotal);
//        BeanUtils.copyProperties(doc, rsp);

        return mergeChunkRespVO;
    }

    /**
     * 校验文件的完整性
     *
     * @param mergeFile
     * @param md5
     * @return
     */
    private boolean checkFileMd5(File mergeFile, String md5) {

        try {
            //创建文件输入流
            FileInputStream inputStream = new FileInputStream(mergeFile);
            //得到文件的md5
            String md5Hex = DigestUtils.md5Hex(inputStream);

            //和传入的md5比较
            if (md5.equalsIgnoreCase(md5Hex)) {
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;

    }


    /**
     * 文件合并
     *
     * @param chunkFileList
     * @param mergeFile
     * @return
     */
    private File mergeFile(List<File> chunkFileList, File mergeFile) {
        try {
            //如果合并文件已存在则删除，否则创建新文件
            if (mergeFile.exists()) {
                mergeFile.delete();
            } else {
                //创建一个新文件
                mergeFile.createNewFile();
            }

            //对块文件进行排序
            Collections.sort(chunkFileList, new Comparator<File>() {
                @Override
                public int compare(File o1, File o2) {
                    if (Integer.parseInt(o1.getName()) > Integer.parseInt(o2.getName())) {
                        return 1;
                    }
                    return -1;

                }
            });
            //创建一个写对象
            RandomAccessFile rafWrite = new RandomAccessFile(mergeFile, "rw");
            byte[] b = new byte[1024];
            for (File chunkFile : chunkFileList) {
                RandomAccessFile rafRead = new RandomAccessFile(chunkFile, "r");
                int len = -1;
                while ((len = rafRead.read(b)) != -1) {
                    rafWrite.write(b, 0, len);
                }
                rafRead.close();
            }
            rafWrite.close();
            return mergeFile;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 获取文件所属目录路径
     *
     * @param fileMd5 文件md5值
     * @return
     */
    private String getFileFolderPath(String fileMd5) {
        return getUploadPath(UPLOAD_PATH) + fileMd5.substring(0, 1) + File.separator + fileMd5.substring(1, 2) + File.separator + fileMd5 + File.separator;
    }

    /**
     * 获取文件的路径
     *
     * @param fileMd5 文件md5值
     * @param fileExt 文件扩展名
     * @return
     */
    private String getFilePath(String fileMd5, String fileExt) {
        return getUploadPath(UPLOAD_PATH) + fileMd5.substring(0, 1) + File.separator + fileMd5.substring(1, 2) + File.separator + fileMd5 + File.separator + fileMd5 + "." + fileExt;
    }


    /**
     * 获取分块文件所在目录路径
     *
     * @param fileMd5
     * @return
     */
    private String getChunkFileFolderPath(String fileMd5) {
        return getUploadPath(UPLOAD_PATH) + fileMd5.substring(0, 1) + File.separator + fileMd5.substring(1, 2) + File.separator + fileMd5 + File.separator + "chunk" + File.separator;
    }

    private String getUploadPath(String uploadPath) {
        String tmpPath = FileUtil.getTmpDirPath();
        if (StringUtils.isNotBlank(uploadPath)) {
            tmpPath = uploadPath;
        }
        return tmpPath;
    }
}


